/* Copyright 2014 Jan Wolter */
/* This is defective.  It fails on board 1, which is solvable. */
#include "stack.h"
#include "tableau.h"
#include "found.h"
#include "stock.h"
#include "waste.h"
#include "solve.h"
#include "game.h"

char *gamename= "Thirty Six";
char *gameopt= "";
Stack *gathered= NULL;
Stack *temp= NULL;

int nplayed= 0;
int nwon= 0;
int nabandoned= 0;
int cardsleft= 0;
int maxoff;
int baserank;

#define SORT

void setopt(char c)
{
}

/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{
    /* Could set things like ndeck, nsuit here */

    /* Build and shuffle the deck */
    Stack *shuf= mixcards(arif, over, cut);

    /* Create the foundations */
    superfound= sf_acebase;
    for (int s= 0; s < nsuit; s++)
	makeFound(1, s, +1, 13);

    /* Create the tableau */
    for (int i= 1; i <= 6; i++)
	makeTableau(6, FLIP_NONE, shuf,
	    S_ANYTHING, -1, S_ANYTHING, -1, 1, F_ANYTHING,
	    BREAK_ANYWHERE, 0);

    /* Create the waste */
    makeWaste();

    /* Create the stock */
    makeStock(DT_WASTE, 1, 1, shuf);

    freeStack(shuf);

    if (verbose > 1) printState(stdout, stacks);

    maxoff= 0;
    int win= solve(gather,0);

    if (verbose > 0) printf("maxoff=%d\n",maxoff);
    nplayed++;
    cardsleft+= maxoff;
    if (win == 1)
	 nwon++;
    else if (win == 2)
	nabandoned++;

    cleanFound();
    cleanTableau();
    cleanWaste();
    cleanStock();
    cleanStacks();
    cleanVisited();
}

void gatherAll()
{
    if (gathered == NULL)
	gathered= allocStack(ncard);
    else
	gathered->n= 0;

    gatherFound(gathered);
    gatherTableau(gathered);
    gatherStock(gathered);
    gatherWaste(gathered,0);
    if (gathered->n != ncard)
    {
	printf("Gather Failed - found %d cards\n",gathered->n);
	exit(1);
    }
}

/* How good is a move? Returns 0 if the move is completely safe and should
 * be made immediately without counting this position a a choice point for
 * backtracking. Positive values are lower for better moves. Negative values
 * mean the move should not be done at all.
 *
 * 0 = move card to foundation when all lower cards are off (safe move).
 * 0 = deal card to empty waste.
 * 1 = move other cards to foundation.
 * 2 = empty a column.
 * 3 = card on card of same suit with card that wasn't on top of predecessor
 * 4 = card on card of different suit that wasn't on top of predecessor
 * 5 = fill space with card that wasn't on top of predecessor
 * 6 = card on card of same suit with card that was on top of predecessor
 * 7 = card on card of different suit that was on top of predecessor
 * 8 = fill space with card that was on top of predecessor
 * 9 = deal card to non-empty waste.
 */
int rateMove(Stack *src, int index1, int len, Stack *dst)
{
    /* Deals are our least favorite move, unless the waste is empty */
    if (src->type == STOCK)
	return (stacks[waste[0].id].n == 0) ? 0 : 9;

    /* Only safe to move a card to the foundation in Thirty Six if all
     * lower cards are off */
    Card *c1= src->c[index1];
    if (dst->type == FOUND)
    {
	if (c1->n <= 2) return 0;
	for (int k= 0; k < nfound; k++)
	    if (stacks[foundation[k].id].n < c1->n-1) return 1;
	return 0;
    }

    /* We like emptying columns */
    if (src->type == TABLEAU && index1 == 0) return 2;

    Card *c2= dst->c[dst->n-1];

    /* Don't break up sorted cards - BREAK_NEVER almost does this for us,
     * but it still allows the single top card to be moved so it can be put
     * on the foundation. That's OK, but we don't want to break a sorted
     * sequence to put it anywhere else.
     */
    Card *c3= (src->type == TABLEAU && index1 > 0) ? src->c[index1-1] : NULL;
    int onpred= (c3 && c3->n == c1->n+1) ? 3 : 0;
    if (c3 && c3->s == c1->s && onpred) return -1;

    /* Move into empty tableau spaces are discouraged */
    if (dst->n == 0) return 5+onpred;

    return (c1->s == c2->s ? 3 : 4) + onpred;
}

void printState(FILE *f, Stack *stks)
{
    printFound(f, stks);
    printTableau(f, stks);
    printStock(f, stks);
    printWaste(f, stks);
}

int victory()
{
    int off= nOff();
    if (off > maxoff) maxoff= off;
    return (off == ncard);
}

/* Return a quality rating for the current solution. Bigger is better. Usually
 * it's just cards off. This is used to decide which solution to report in
 * cases where we never win.
 */
int quality()
{
    return nOff();
}

void summary()
{
    printf("Played:    %d games\n",nplayed);
    printf("Won:       %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Abandoned: %d games (%.2f%)\n",nabandoned,(float)nabandoned*100/nplayed);
    printf("Cards Off: %.2f\n",(float)cardsleft/nplayed);
}

/* Comparision function for stack order normalization in currstate() */
static int cmpstacks(const void *p1, const void *p2)
{
    const Stack *s1= *(const Stack **)p1;
    const Stack *s2= *(const Stack **)p2;
    if (s1->n == 0 || s2->n == 0)
	return s2->n - s1->n;
    int c1= s1->c[0] - deck;
    int c2= s2->c[0] - deck;
    return c2 - c1;
}

/* Return a string that uniquely describes the current state. */
char *currstate()
{
    /* ncards for all the cards on things, 4 for tableaus, 1 for reserve,
     * 4 for list terminators.
     */
    char buf[ncard+20], *p= buf;

    /* Numbers of cards in each foundation pile */
    /* We add one to each count because we don't want any zeros */
    for (int k= 0; k < nfound; k++)
	*(p++)= stacks[foundation[k].id].n + 1;

    /* Number of cards in the stock */
    *(p++)= stacks[stock[0].id].n + 1;

    /* List of cards on each tableau pile */
#ifdef SORT
    /* We normalize the order of the tableau piles, sorting them by the
     * first card on them. Very helpful for Indefatigable.
     */
    Stack *norm[12];
    for (int k= 0; k < ntableau; k++)
	norm[k]= stacks + tableau[k].id;
    qsort(norm, ntableau, sizeof(Stack *), cmpstacks);

    for (int k= 0; k < ntableau; k++)
    {
	Stack *s= norm[k];
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }
#else
    /* Non-normalized case */
    for (int k= 0; k < ntableau; k++)
    {
	Stack *s= stacks + tableau[k].id;
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }
#endif

    *p= '\0';

    return strdup(buf);
}
